import React, { useRef, useEffect } from "react";
import {
  Attachments,
  Bubble,
  Conversations,
  Prompts,
  Sender,
  Welcome,
  useXAgent,
  useXChat,
} from "@ant-design/x";
import {
  CloudUploadOutlined,
  CommentOutlined,
  FireOutlined,
  HeartOutlined,
  PaperClipOutlined,
  PlusOutlined,
  ReadOutlined,
  SmileOutlined,
  UserOutlined,
  OpenAIOutlined,
  CloseOutlined,
} from "@ant-design/icons";
import { Badge, Button, Space, Typography } from "antd";
import type { GetProp } from "antd";
import PdfPreviewer from "./components/PdfPreviewer";
import styles from "./index.module.less";
import { fetchEventSource } from "@microsoft/fetch-event-source";
import markdownit from "markdown-it";

const md = markdownit({ html: true, breaks: true });

// API 配置
const BASE_URL = "http://1.203.103.140:8777";
const PATH_TEST = "/api/local_doc_qa/stream_test";

/**
 * 渲染 Markdown 内容
 */
const renderMarkdown = (content: string) => {
  // 使用正则表达式匹配[^n^]模式
  const regex = /\[\^(\d+)\^\]/g;
  // 替换函数
  const result = content.replace(regex, (match, number) => {
    number++;
    return `<span class="answer_source_tag" tagnumber="${number}" tagindex="${number}">${number}</span>`;
  });

  return (
    <Typography>
      <div dangerouslySetInnerHTML={{ __html: md.render(result) }} />
    </Typography>
  );
};

/**
 * 消息类型定义
 */
interface Message {
  id: number;
  loading: boolean;
  message: string;
  status: "user" | "ai";
}

/**
 * 流式响应消息类型
 */
interface StreamMessage {
  data: string;
  event?: string;
}

/**
 * 参考文档类型
 */
interface SourceDocument {
  text: string;
  title: string;
  publish_date: string;
}

/**
 * 响应内容类型
 */
interface ResponseContent {
  id: string;
  content: string;
  final_source?: SourceDocument[];
}

/**
 * 思考过程响应类型
 */
interface ThinkContent extends ResponseContent {
  id: "_think" | "_cot" | "answer_source";
}

/**
 * 渲染标题
 */
const renderTitle = (icon: React.ReactElement, title: string) => (
  <Space align="start">
    {icon}
    <span>{title}</span>
  </Space>
);

const defaultConversationsItems = [
  {
    key: "0",
    label: "未来一年白酒股行情如何?",
  },
];

const placeholderPromptsItems = [
  {
    key: "1",
    label: renderTitle(<FireOutlined style={{ color: "#FF4D4F" }} />, "热点"),
    description: "你对什么感兴趣？",
    children: [
      {
        key: "1-1",
        description: "白酒板块表现如何？",
      },
      {
        key: "1-2",
        description: "CPI指数和白酒销量什么关系？",
      },
      {
        key: "1-3",
        description: "PPI和白酒销量什么关系呢？",
      },
    ],
  },
  {
    key: "2",
    label: renderTitle(<ReadOutlined style={{ color: "#1890FF" }} />, "指引"),
    description: "你想知道哪个行业?",
    children: [
      {
        key: "2-1",
        icon: <HeartOutlined />,
        description: "白酒行业",
      },
      {
        key: "2-2",
        icon: <SmileOutlined />,
        description: "金融行业",
      },
      {
        key: "2-3",
        icon: <CommentOutlined />,
        description: "房地产行业",
      },
    ],
  },
];

const senderPromptsItems = [
  {
    key: "1",
    description: "热点",
    icon: <FireOutlined style={{ color: "#FF4D4F" }} />,
  },
  {
    key: "2",
    description: "指引",
    icon: <ReadOutlined style={{ color: "#1890FF" }} />,
  },
];

// 添加 markdown-it 类型声明
declare module "markdown-it" {
  interface MarkdownIt {
    render(text: string): string;
  }

  interface MarkdownItConstructor {
    new (options?: { html?: boolean; breaks?: boolean }): MarkdownIt;
  }

  const MarkdownIt: MarkdownItConstructor;
  export default MarkdownIt;
}

const ChatPage: React.FC = () => {
  const [headerOpen, setHeaderOpen] = React.useState(false);
  const [content, setContent] = React.useState("");
  const [showPdf, setShowPdf] = React.useState(true);
  const [conversationsItems, setConversationsItems] = React.useState(
    defaultConversationsItems
  );
  const [activeKey, setActiveKey] = React.useState(
    defaultConversationsItems[0].key
  );
  const [attachedFiles, setAttachedFiles] = React.useState<
    GetProp<typeof Attachments, "items">
  >([]);
  const [testMsg, setTestMsg] = React.useState<Message[]>([]);
  const [strRes, setStrRes] = React.useState<Record<string, StreamMessage[]>>(
    {}
  );
  const scrollContainerRef = useRef<HTMLDivElement>(null);

  // 配置 AI Agent
  const [agent] = useXAgent({
    baseURL: BASE_URL,
  });

  useXChat<string>({
    agent,
  });

  /**
   * 处理流式响应消息
   */
  const handleStreamMessage = (q: string) => {
    class RetriableError extends Error {}
    class FatalError extends Error {}
    const ctrl = new AbortController();

    // 生成唯一对话ID
    const newAiId = Date.now();
    const copyA: Message[] = [
      {
        id: testMsg.length + 1,
        loading: false,
        message: q,
        status: "user",
      },
      {
        id: newAiId,
        loading: false,
        message: "",
        status: "ai",
      },
    ];

    setTestMsg((prevTestMsg: Message[]) => [...prevTestMsg, ...copyA]);
    setStrRes((prev) => ({ ...prev, [newAiId]: [] }));

    const params = JSON.stringify({ message: q });

    fetchEventSource(BASE_URL + PATH_TEST, {
      method: "POST",
      signal: ctrl.signal,
      headers: { "Content-Type": "application/json" },
      openWhenHidden: true,
      body: params,
      async onopen(response) {
        if (
          response.ok &&
          response.headers.get("content-type") === "text/event-stream"
        ) {
          return;
        } else if (
          response.status >= 400 &&
          response.status < 500 &&
          response.status !== 429
        ) {
          throw new FatalError();
        } else {
          throw new RetriableError();
        }
      },
      onmessage(msg) {
        const id = newAiId;
        setStrRes((prevStrRes) => ({
          ...prevStrRes,
          [id]: [...(prevStrRes[id] || []), msg],
        }));

        if (msg.event === "FatalError") {
          throw new FatalError(msg.data);
        }
      },
      onclose() {
        ctrl.abort();
        throw new RetriableError();
      },
      onerror(err) {
        ctrl.abort();
        if (err instanceof FatalError) {
          throw err;
        }
      },
    });
  };

  // 处理消息发送
  const onSubmit = (nextContent: string) => {
    if (!nextContent) return;
    handleStreamMessage(nextContent);
    setContent("");
  };

  // 处理提示词点击
  const onPromptsItemClick: GetProp<typeof Prompts, "onItemClick"> = (info) => {
    handleStreamMessage(info.data.description as string);
  };

  // 处理新建会话
  const onAddConversation = () => {
    setConversationsItems([
      ...conversationsItems,
      {
        key: `${conversationsItems.length}`,
        label: "新会话",
      },
    ]);
    setActiveKey(`${conversationsItems.length}`);
  };

  // 处理会话切换
  const onConversationClick = (key: string) => {
    setActiveKey(key);
  };

  // 处理文件变更
  const handleFileChange: GetProp<typeof Attachments, "onChange"> = (info) =>
    setAttachedFiles(info.fileList);

  // 处理文件点击
  const handleFileItemClick = (el: SourceDocument) => {
    console.log("handleFileItemClick", el);
  };

  // 欢迎页面节点
  const placeholderNode = (
    <Space direction="vertical" size={16} className={styles.placeholder}>
      <Welcome
        variant="borderless"
        icon="/ai-finance-logo.png"
        title="AI金融助手"
        description="您的智能金融顾问，为您提供专业的金融分析和建议"
      />
      <Prompts
        title="猜你想提问"
        items={placeholderPromptsItems}
        onItemClick={onPromptsItemClick}
      />
    </Space>
  );

  // 附件按钮节点
  const attachmentsNode = (
    <Badge dot={attachedFiles.length > 0 && !headerOpen}>
      <Button
        type="text"
        icon={<PaperClipOutlined />}
        onClick={() => setHeaderOpen(!headerOpen)}
      />
    </Badge>
  );

  // 发送器头部节点
  const senderHeader = (
    <Sender.Header title="附件" open={headerOpen} onOpenChange={setHeaderOpen}>
      <Attachments
        beforeUpload={() => false}
        items={attachedFiles}
        onChange={handleFileChange}
        placeholder={(type) =>
          type === "drop"
            ? { title: "将文件放到此处" }
            : {
                icon: <CloudUploadOutlined />,
                title: "上传文件",
                description: "单击或拖动文件到此区域进行上传",
              }
        }
      />
    </Sender.Header>
  );

  // Logo 节点
  const logoNode = (
    <div className={styles.logo}>
      <img src="/ai-finance-logo.png" alt="logo" />
      <span>AI金融助手</span>
    </div>
  );

  useEffect(() => {
    if (scrollContainerRef.current) {
      scrollContainerRef.current.scrollTop =
        scrollContainerRef.current.scrollHeight;
    }
  }, [testMsg, strRes]);

  useEffect(() => {
    const handleClick = (event: MouseEvent) => {
      const fileItem = (event.target as HTMLElement).closest(".file-item");
      if (fileItem) {
        const el = JSON.parse(fileItem.getAttribute("data-el") || "{}");
        handleFileItemClick(el);
      }
    };

    const handleThinkConClick = (event: MouseEvent) => {
      const thinkCon = (event.target as HTMLElement).closest(".think-con");
      if (thinkCon) {
        thinkCon.classList.toggle("collapsed");
      }
    };

    document.addEventListener("click", handleClick);
    document.addEventListener("click", handleThinkConClick);

    return () => {
      document.removeEventListener("click", handleClick);
      document.removeEventListener("click", handleThinkConClick);
    };
  }, []);

  return (
    <div className={styles.layout}>
      <div className={styles.menu}>
        {logoNode}
        <Button
          onClick={onAddConversation}
          type="link"
          className={styles.addBtn}
          icon={<PlusOutlined />}
        >
          新建会话
        </Button>
        <Conversations
          items={conversationsItems}
          className={styles.conversations}
          activeKey={activeKey}
          onActiveChange={onConversationClick}
        />
      </div>
      <div className={styles.layoutMain}>
        <div
          className={`${styles.mainContent} ${
            showPdf ? styles.withPdf : styles.withoutPdf
          }`}
        >
          <div className={styles.chat}>
            <div className={styles.chatCon} ref={scrollContainerRef}>
              {testMsg.length > 0
                ? testMsg.map((el: Message) => (
                    <Bubble
                      key={el.id}
                      variant="outlined"
                      loading={
                        el.status === "user"
                          ? false
                          : strRes[el.id]?.length > 0
                          ? false
                          : true
                      }
                      placement={el.status === "user" ? "end" : "start"}
                      avatar={
                        el.status === "ai"
                          ? {
                              icon: <OpenAIOutlined />,
                              style: { background: "#fde3cf" },
                            }
                          : {
                              icon: <UserOutlined />,
                              style: { background: "#87d068" },
                            }
                      }
                      messageRender={renderMarkdown}
                      content={
                        el.status === "user"
                          ? el.message
                          : strRes[el.id]
                              ?.map((line: StreamMessage, idx: number) => {
                                const res = JSON.parse(
                                  line.data
                                ) as ThinkContent;
                                // 思考区域处理
                                if (idx > 0) {
                                  const preRes = JSON.parse(
                                    strRes[el.id][idx - 1].data
                                  ) as ThinkContent;
                                  if (
                                    preRes.id !== "_think" &&
                                    res.id === "_think"
                                  ) {
                                    res.content =
                                      '<div class="think-con">' + res.content;
                                  }
                                }
                                if (idx === 0 && res.id === "_think") {
                                  res.content =
                                    '<div class="think-con">' + res.content;
                                }

                                // 正文区域处理
                                if (strRes[el.id][idx + 1]) {
                                  const nextRes = JSON.parse(
                                    strRes[el.id][idx + 1].data
                                  ) as ThinkContent;
                                  if (
                                    nextRes.id !== "_think" &&
                                    res.id === "_think"
                                  ) {
                                    res.content =
                                      res.content +
                                      '</div><div class="answer-con">';
                                  }
                                  if (
                                    nextRes.id !== "_cot" &&
                                    res.id === "_cot"
                                  ) {
                                    res.content = res.content + "</div>";
                                  }
                                }

                                // 参考文档处理
                                if (
                                  res.id === "answer_source" &&
                                  res.final_source
                                ) {
                                  const title_str =
                                    '<span class="title">参考文档</span>';
                                  const file_con = res.final_source
                                    .map(
                                      (doc: SourceDocument, index: number) => {
                                        return `<div class="file-item" data-el='${JSON.stringify(
                                          doc
                                        )}'><div class="file-t"><span class="tag">${
                                          index + 1
                                        }</span><span class="text">${
                                          doc.text
                                        }</span></div><div class="file-b"><span class="title" title="${
                                          doc.title
                                        }">${
                                          doc.title
                                        }</span><span class="publish-date">${
                                          doc.publish_date
                                        }</span></div></div>`;
                                      }
                                    )
                                    .join("");
                                  return `<div class="source-con">${title_str}<div class="file-list">${file_con}</div></div>`;
                                }
                                return res.content;
                              })
                              .join("")
                      }
                    />
                  ))
                : placeholderNode}
            </div>
            <Prompts
              items={senderPromptsItems}
              onItemClick={onPromptsItemClick}
            />
            <Sender
              value={content}
              header={senderHeader}
              onSubmit={onSubmit}
              onChange={setContent}
              prefix={attachmentsNode}
              loading={agent.isRequesting()}
              className={styles.sender}
            />
          </div>
          {showPdf && (
            <div className={styles.pdfPreview}>
              <Button
                type="text"
                icon={<CloseOutlined />}
                className={styles.closeBtn}
                onClick={() => setShowPdf(false)}
              />
              <PdfPreviewer />
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

export default ChatPage;
